راهنمای جامع دسترسی به دادههای فریم ویدیو با صفحات VideoFrame در WebCodecs. با فرمتهای پیکسل، چیدمان حافظه و کاربردهای پردازش پیشرفته ویدیو در مرورگر آشنا شوید.
صفحههای VideoFrame در WebCodecs: نگاهی عمیق به دسترسی به دادههای فریم ویدیو
WebCodecs نشاندهنده یک تغییر پارادایم در پردازش رسانه مبتنی بر وب است. این API دسترسی سطح پایین به اجزای سازنده رسانه را فراهم میکند و به توسعهدهندگان امکان میدهد تا برنامههای پیچیدهای را مستقیماً در مرورگر ایجاد کنند. یکی از قدرتمندترین ویژگیهای WebCodecs، شیء VideoFrame و در داخل آن، صفحههای VideoFrame است که دادههای خام پیکسل فریمهای ویدیو را در اختیار قرار میدهند. این مقاله یک راهنمای جامع برای درک و استفاده از صفحههای VideoFrame برای دستکاری پیشرفته ویدیو ارائه میدهد.
درک شیء VideoFrame
قبل از پرداختن به صفحهها، بیایید خود شیء VideoFrame را مرور کنیم. یک VideoFrame نمایانگر یک فریم از ویدیو است. این شیء دادههای ویدیویی رمزگشایی شده (یا رمزگذاری شده) را به همراه فرادادههای مرتبط مانند برچسب زمانی (timestamp)، مدت زمان (duration) و اطلاعات فرمت در بر میگیرد. API مربوط به VideoFrame متدهایی برای موارد زیر ارائه میدهد:
- خواندن دادههای پیکسل: اینجاست که صفحهها وارد عمل میشوند.
- کپی کردن فریمها: ایجاد اشیاء
VideoFrameجدید از روی موارد موجود. - بستن فریمها: آزاد کردن منابع زیربنایی که توسط فریم نگهداری میشوند.
شیء VideoFrame در طول فرآیند رمزگشایی، معمولاً توسط یک VideoDecoder، یا به صورت دستی هنگام ایجاد یک فریم سفارشی، ایجاد میشود.
صفحههای VideoFrame چه هستند؟
دادههای پیکسل یک VideoFrame اغلب در چندین صفحه سازماندهی میشوند، به ویژه در فرمتهایی مانند YUV. هر صفحه نمایانگر یک مؤلفه متفاوت از تصویر است. به عنوان مثال، در فرمت YUV420، سه صفحه وجود دارد:
- Y (Luma): نشاندهنده روشنایی (luminance) تصویر است. این صفحه اطلاعات سیاهوسفید را در خود دارد.
- U (Cb): نشاندهنده مؤلفه رنگی تفاوت آبی (blue-difference chroma) است.
- V (Cr): نشاندهنده مؤلفه رنگی تفاوت قرمز (red-difference chroma) است.
فرمتهای RGB، اگرچه در ظاهر سادهتر هستند، ممکن است در برخی موارد از چندین صفحه استفاده کنند. تعداد صفحهها و معنای آنها کاملاً به VideoPixelFormat مربوط به VideoFrame بستگی دارد.
مزیت استفاده از صفحهها این است که امکان دسترسی و دستکاری کارآمد مؤلفههای رنگی خاص را فراهم میکند. به عنوان مثال، ممکن است بخواهید فقط روشنایی (صفحه Y) را بدون تأثیر بر رنگ (صفحههای U و V) تنظیم کنید.
دسترسی به صفحههای VideoFrame: API
API مربوط به VideoFrame متدهای زیر را برای دسترسی به دادههای صفحه فراهم میکند:
copyTo(destination, options): محتوایVideoFrameرا به یک مقصد کپی میکند که میتواند یکVideoFrameدیگر، یکCanvasImageBitmapیا یکArrayBufferViewباشد. شیءoptionsکنترل میکند که کدام صفحهها و چگونه کپی شوند. این مکانیزم اصلی برای دسترسی به صفحهها است.
شیء options در متد copyTo به شما امکان میدهد تا چیدمان و مقصد دادههای فریم ویدیو را مشخص کنید. ویژگیهای کلیدی عبارتند از:
format: فرمت پیکسل مورد نظر برای دادههای کپی شده. این میتواند همان فرمتVideoFrameاصلی یا یک فرمت متفاوت باشد (مثلاً تبدیل از YUV به RGB).codedWidthوcodedHeight: عرض و ارتفاع فریم ویدیو بر حسب پیکسل.layout: آرایهای از اشیاء که چیدمان هر صفحه در حافظه را توصیف میکند. هر شیء در آرایه موارد زیر را مشخص میکند:offset: آفست (فاصله)، بر حسب بایت، از ابتدای بافر داده تا شروع دادههای صفحه.stride: تعداد بایتها بین شروع هر ردیف در صفحه. این برای مدیریت padding بسیار مهم است.
بیایید به مثالی از کپی کردن یک VideoFrame با فرمت YUV420 به یک بافر خام نگاه کنیم:
async function copyYUV420ToBuffer(videoFrame, buffer) {
const width = videoFrame.codedWidth;
const height = videoFrame.codedHeight;
// YUV420 دارای 3 صفحه است: Y، U و V
const yPlaneSize = width * height;
const uvPlaneSize = width * height / 4;
const layout = [
{ offset: 0, stride: width }, // صفحه Y
{ offset: yPlaneSize, stride: width / 2 }, // صفحه U
{ offset: yPlaneSize + uvPlaneSize, stride: width / 2 } // صفحه V
];
await videoFrame.copyTo(buffer, {
format: 'I420',
codedWidth: width,
codedHeight: height,
layout: layout
});
videoFrame.close(); // آزاد کردن منابع مهم است
}
توضیحات:
- ما اندازه هر صفحه را بر اساس
widthوheightمحاسبه میکنیم. Y دارای رزولوشن کامل است، در حالی که U و V نمونهبرداری کاهشی شدهاند (4:2:0). - آرایه
layoutچیدمان حافظه را تعریف میکند.offsetمشخص میکند که هر صفحه از کجا در بافر شروع میشود وstrideتعداد بایتهایی را که باید برای رفتن به ردیف بعدی در آن صفحه پرش کرد، مشخص میکند. - گزینه
formatروی 'I420' تنظیم شده است که یک فرمت رایج YUV420 است. - نکته بسیار مهم این است که پس از کپی،
videoFrame.close()برای آزاد کردن منابع فراخوانی میشود.
فرمتهای پیکسل: دنیایی از امکانات
درک فرمتهای پیکسل برای کار با صفحههای VideoFrame ضروری است. VideoPixelFormat نحوه رمزگذاری اطلاعات رنگی در فریم ویدیو را تعریف میکند. در اینجا برخی از فرمتهای پیکسل رایج که ممکن است با آنها روبرو شوید آورده شده است:
- I420 (YUV420p): یک فرمت صفحهای YUV که در آن مؤلفههای Y، U و V در صفحههای جداگانه ذخیره میشوند. U و V با ضریب 2 در هر دو بعد افقی و عمودی نمونهبرداری کاهشی شدهاند. این یک فرمت بسیار رایج و کارآمد است.
- NV12 (YUV420sp): یک فرمت نیمهصفحهای YUV که در آن Y در یک صفحه ذخیره میشود و مؤلفههای U و V به صورت درهمآمیخته در صفحه دوم ذخیره میشوند.
- RGBA: مؤلفههای قرمز، سبز، آبی و آلفا در یک صفحه واحد ذخیره میشوند، معمولاً با 8 بیت برای هر مؤلفه (32 بیت در هر پیکسل). ترتیب مؤلفهها میتواند متفاوت باشد (مثلاً BGRA).
- RGB565: مؤلفههای قرمز، سبز و آبی در یک صفحه واحد با 5 بیت برای قرمز، 6 بیت برای سبز و 5 بیت برای آبی (16 بیت در هر پیکسل) ذخیره میشوند.
- GRAYSCALE: تصاویر سیاهوسفید را با یک مقدار روشنایی (luma) برای هر پیکسل نشان میدهد.
ویژگی VideoFrame.format به شما فرمت پیکسل یک فریم معین را میگوید. حتماً قبل از تلاش برای دسترسی به صفحهها، این ویژگی را بررسی کنید. برای لیست کامل فرمتهای پشتیبانیشده میتوانید به مشخصات WebCodecs مراجعه کنید.
موارد استفاده عملی
دسترسی به صفحههای VideoFrame طیف گستردهای از امکانات را برای پردازش پیشرفته ویدیو در مرورگر باز میکند. در اینجا چند نمونه آورده شده است:
۱. جلوههای ویدیویی زنده
شما میتوانید با دستکاری دادههای پیکسل در VideoFrame، جلوههای ویدیویی زنده اعمال کنید. به عنوان مثال، میتوانید با میانگینگیری از مؤلفههای R، G و B هر پیکسل در یک فریم RGBA و سپس تنظیم هر سه مؤلفه به آن مقدار میانگین، یک فیلتر سیاهوسفید پیادهسازی کنید. همچنین میتوانید یک افکت سپیا (sepia) ایجاد کنید یا روشنایی و کنتراست را تنظیم کنید.
async function applyGrayscale(videoFrame) {
const width = videoFrame.codedWidth;
const height = videoFrame.codedHeight;
const buffer = new ArrayBuffer(width * height * 4); // RGBA
const rgba = new Uint8ClampedArray(buffer);
await videoFrame.copyTo(rgba, {
format: 'RGBA',
codedWidth: width,
codedHeight: height
});
for (let i = 0; i < rgba.length; i += 4) {
const r = rgba[i];
const g = rgba[i + 1];
const b = rgba[i + 2];
const gray = (r + g + b) / 3;
rgba[i] = gray; // Red
rgba[i + 1] = gray; // Green
rgba[i + 2] = gray; // Blue
}
// ایجاد یک VideoFrame جدید از دادههای تغییر یافته.
const newFrame = new VideoFrame(rgba, {
format: 'RGBA',
codedWidth: width,
codedHeight: height,
timestamp: videoFrame.timestamp,
duration: videoFrame.duration
});
videoFrame.close(); // آزاد کردن فریم اصلی
return newFrame;
}
۲. کاربردهای بینایی کامپیوتر
صفحههای VideoFrame دسترسی مستقیم به دادههای پیکسل مورد نیاز برای وظایف بینایی کامپیوتر را فراهم میکنند. میتوانید از این دادهها برای پیادهسازی الگوریتمهای تشخیص اشیاء، تشخیص چهره، ردیابی حرکت و موارد دیگر استفاده کنید. میتوانید از WebAssembly برای بخشهای حساس به عملکرد کد خود بهره ببرید.
به عنوان مثال، میتوانید یک VideoFrame رنگی را به سیاهوسفید تبدیل کرده و سپس یک الگوریتم تشخیص لبه (مانند عملگر سوبل) را برای شناسایی لبهها در تصویر اعمال کنید. این میتواند به عنوان یک مرحله پیشپردازش برای تشخیص اشیاء استفاده شود.
۳. ویرایش و ترکیببندی ویدیو
شما میتوانید از صفحههای VideoFrame برای پیادهسازی ویژگیهای ویرایش ویدیو مانند برش، تغییر اندازه، چرخش و ترکیببندی استفاده کنید. با دستکاری مستقیم دادههای پیکسل، میتوانید انتقالها و جلوههای سفارشی ایجاد کنید.
به عنوان مثال، میتوانید با کپی کردن تنها بخشی از دادههای پیکسل به یک VideoFrame جدید، یک VideoFrame را برش دهید. برای این کار باید آفستها و strideهای layout را متناسب با آن تنظیم کنید.
۴. کدکهای سفارشی و ترنسکدینگ
در حالی که WebCodecs پشتیبانی داخلی برای کدکهای رایج مانند AV1، VP9 و H.264 را فراهم میکند، شما همچنین میتوانید از آن برای پیادهسازی کدکهای سفارشی یا خطوط لوله ترنسکدینگ استفاده کنید. شما باید فرآیند رمزگذاری و رمزگشایی را خودتان مدیریت کنید، اما صفحههای VideoFrame به شما امکان دسترسی و دستکاری دادههای خام پیکسل را میدهند. این میتواند برای فرمتهای ویدیویی خاص یا نیازمندیهای رمزگذاری تخصصی مفید باشد.
۵. تحلیلهای پیشرفته
با دسترسی به دادههای پیکسل زیربنایی، میتوانید تحلیل عمیقی از محتوای ویدیو انجام دهید. این شامل وظایفی مانند اندازهگیری میانگین روشنایی یک صحنه، شناسایی رنگهای غالب یا تشخیص تغییرات در محتوای صحنه است. این میتواند برنامههای کاربردی تحلیل ویدیوی پیشرفته برای امنیت، نظارت یا تحلیل محتوا را امکانپذیر سازد.
کار با Canvas و WebGL
در حالی که میتوانید دادههای پیکسل را مستقیماً در صفحههای VideoFrame دستکاری کنید، اغلب نیاز دارید که نتیجه را روی صفحه نمایش دهید. رابط CanvasImageBitmap پلی بین VideoFrame و عنصر <canvas> فراهم میکند. میتوانید یک CanvasImageBitmap از یک VideoFrame ایجاد کرده و سپس آن را با استفاده از متد drawImage() روی بوم نقاشی کنید.
async function renderVideoFrameToCanvas(videoFrame, canvas) {
const bitmap = await createImageBitmap(videoFrame);
const ctx = canvas.getContext('2d');
ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
bitmap.close(); // آزاد کردن منابع bitmap
videoFrame.close(); // آزاد کردن منابع VideoFrame
}
برای رندرینگ پیشرفتهتر، میتوانید از WebGL استفاده کنید. میتوانید دادههای پیکسل از صفحههای VideoFrame را به تکسچرهای WebGL آپلود کرده و سپس از شیدرها برای اعمال جلوهها و تبدیلها استفاده کنید. این به شما امکان میدهد تا از GPU برای پردازش ویدیوی با کارایی بالا بهره ببرید.
ملاحظات عملکرد
کار با دادههای خام پیکسل میتواند از نظر محاسباتی سنگین باشد، بنابراین در نظر گرفتن بهینهسازی عملکرد بسیار مهم است. در اینجا چند نکته آورده شده است:
- به حداقل رساندن کپیها: از کپی کردن غیرضروری دادههای پیکسل خودداری کنید. سعی کنید در صورت امکان عملیات را به صورت درجا (in-place) انجام دهید.
- استفاده از WebAssembly: برای بخشهای حساس به عملکرد کد خود، استفاده از WebAssembly را در نظر بگیرید. WebAssembly میتواند عملکردی نزدیک به بومی برای وظایف محاسباتی سنگین فراهم کند.
- بهینهسازی چیدمان حافظه: فرمت پیکسل و چیدمان حافظه مناسب را برای برنامه خود انتخاب کنید. اگر نیازی به دسترسی مکرر به مؤلفههای رنگی جداگانه ندارید، استفاده از فرمتهای بستهبندی شده (مانند RGBA) را در نظر بگیرید.
- استفاده از OffscreenCanvas: برای پردازش در پسزمینه، از
OffscreenCanvasبرای جلوگیری از مسدود کردن رشته اصلی استفاده کنید. - پروفایل کردن کد: از ابزارهای توسعهدهنده مرورگر برای پروفایل کردن کد خود و شناسایی گلوگاههای عملکرد استفاده کنید.
سازگاری مرورگر
WebCodecs و API مربوط به VideoFrame در اکثر مرورگرهای مدرن، از جمله کروم، فایرفاکس و سافاری پشتیبانی میشوند. با این حال، سطح پشتیبانی ممکن است بسته به نسخه مرورگر و سیستم عامل متفاوت باشد. آخرین جداول سازگاری مرورگرها را در سایتهایی مانند MDN Web Docs بررسی کنید تا اطمینان حاصل کنید که ویژگیهایی که استفاده میکنید در مرورگرهای هدف شما پشتیبانی میشوند. برای سازگاری بین مرورگرها، تشخیص ویژگی (feature detection) توصیه میشود.
مشکلات رایج و عیبیابی
در اینجا برخی از مشکلات رایج که باید هنگام کار با صفحههای VideoFrame از آنها اجتناب کنید آورده شده است:
- چیدمان نادرست: اطمینان حاصل کنید که آرایه
layoutبه درستی چیدمان حافظه دادههای پیکسل را توصیف میکند. آفستها یا strideهای نادرست میتوانند منجر به تصاویر خراب شوند. - فرمتهای پیکسل نامطابق: مطمئن شوید که فرمت پیکسلی که در متد
copyToمشخص میکنید با فرمت واقعیVideoFrameمطابقت دارد. - نشت حافظه: همیشه اشیاء
VideoFrameوCanvasImageBitmapرا پس از اتمام کار با آنها ببندید تا منابع زیربنایی آزاد شوند. عدم انجام این کار میتواند منجر به نشت حافظه شود. - عملیات ناهمزمان: به یاد داشته باشید که
copyToیک عملیات ناهمزمان است. ازawaitاستفاده کنید تا اطمینان حاصل کنید که عملیات کپی قبل از دسترسی به دادههای پیکسل کامل شده است. - محدودیتهای امنیتی: از محدودیتهای امنیتی که ممکن است هنگام دسترسی به دادههای پیکسل از ویدیوهای با منشأ متقابل (cross-origin) اعمال شوند، آگاه باشید.
مثال: تبدیل YUV به RGB
بیایید یک مثال پیچیدهتر را در نظر بگیریم: تبدیل یک VideoFrame با فرمت YUV420 به یک VideoFrame با فرمت RGB. این شامل خواندن صفحههای Y، U و V، تبدیل آنها به مقادیر RGB و سپس ایجاد یک VideoFrame جدید RGB است.
این تبدیل را میتوان با استفاده از فرمول زیر پیادهسازی کرد:
R = Y + 1.402 * (Cr - 128)
G = Y - 0.34414 * (Cb - 128) - 0.71414 * (Cr - 128)
B = Y + 1.772 * (Cb - 128)
کد آن در اینجا آمده است:
async function convertYUV420ToRGBA(videoFrame) {
const width = videoFrame.codedWidth;
const height = videoFrame.codedHeight;
const yPlaneSize = width * height;
const uvPlaneSize = width * height / 4;
const yuvBuffer = new ArrayBuffer(yPlaneSize + 2 * uvPlaneSize);
const yuvPlanes = new Uint8ClampedArray(yuvBuffer);
const layout = [
{ offset: 0, stride: width }, // صفحه Y
{ offset: yPlaneSize, stride: width / 2 }, // صفحه U
{ offset: yPlaneSize + uvPlaneSize, stride: width / 2 } // صفحه V
];
await videoFrame.copyTo(yuvPlanes, {
format: 'I420',
codedWidth: width,
codedHeight: height,
layout: layout
});
const rgbaBuffer = new ArrayBuffer(width * height * 4);
const rgba = new Uint8ClampedArray(rgbaBuffer);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const yIndex = y * width + x;
const uIndex = Math.floor(y / 2) * (width / 2) + Math.floor(x / 2) + yPlaneSize;
const vIndex = Math.floor(y / 2) * (width / 2) + Math.floor(x / 2) + yPlaneSize + uvPlaneSize;
const Y = yuvPlanes[yIndex];
const U = yuvPlanes[uIndex] - 128;
const V = yuvPlanes[vIndex] - 128;
let R = Y + 1.402 * V;
let G = Y - 0.34414 * U - 0.71414 * V;
let B = Y + 1.772 * U;
R = Math.max(0, Math.min(255, R));
G = Math.max(0, Math.min(255, G));
B = Math.max(0, Math.min(255, B));
const rgbaIndex = y * width * 4 + x * 4;
rgba[rgbaIndex] = R;
rgba[rgbaIndex + 1] = G;
rgba[rgbaIndex + 2] = B;
rgba[rgbaIndex + 3] = 255; // Alpha
}
}
const newFrame = new VideoFrame(rgba, {
format: 'RGBA',
codedWidth: width,
codedHeight: height,
timestamp: videoFrame.timestamp,
duration: videoFrame.duration
});
videoFrame.close(); // آزاد کردن فریم اصلی
return newFrame;
}
این مثال قدرت و پیچیدگی کار با صفحههای VideoFrame را نشان میدهد. این کار نیازمند درک خوبی از فرمتهای پیکسل، چیدمان حافظه و تبدیل فضاهای رنگی است.
نتیجهگیری
API مربوط به صفحههای VideoFrame در WebCodecs سطح جدیدی از کنترل بر پردازش ویدیو در مرورگر را باز میکند. با درک نحوه دسترسی و دستکاری مستقیم دادههای پیکسل، میتوانید برنامههای پیشرفتهای برای جلوههای ویدیویی زنده، بینایی کامپیوتر، ویرایش ویدیو و موارد دیگر ایجاد کنید. در حالی که کار با صفحههای VideoFrame میتواند چالشبرانگیز باشد، پاداشهای بالقوه آن قابل توجه است. با ادامه تکامل WebCodecs، بدون شک به ابزاری ضروری برای توسعهدهندگان وب که با رسانه کار میکنند تبدیل خواهد شد.
ما شما را تشویق میکنیم که با API صفحههای VideoFrame آزمایش کنید و قابلیتهای آن را کشف کنید. با درک اصول زیربنایی و به کارگیری بهترین شیوهها، میتوانید برنامههای ویدیویی نوآورانه و با کارایی بالا ایجاد کنید که مرزهای آنچه در مرورگر ممکن است را جابجا میکنند.
برای مطالعه بیشتر
- مستندات MDN Web Docs درباره WebCodecs
- مشخصات فنی WebCodecs
- مخازن کد نمونه WebCodecs در گیتهاب.